#pragma once
#include "stdafx.h"
#include ""

using namespace std;
using namespace xercesc;

namespace xercescHelpingLibrary
{
	const string EmptyString;

	class CXMLElement
	{
		DOMElement* m_element;
	private:
		CXMLElement()
		{
			m_element = NULL;
		}
	public:
		~CXMLElement()
		{
			// DON'T delete m_element, the parser handles deleting the element when the parser gets deleted.
		}
	public:
		static CXMLElement* CreateElement(DOMElement* element)
		{
			if (element==NULL) return NULL;

			CXMLElement* newObject = new CXMLElement();
			newObject->m_element = element;
			return newObject;
		}

		string getName()
		{
			return XMLChToString(m_element->getNodeName());
		}
	private:
		DOMText* getTextContent()
		{
			return dynamic_cast<DOMText*>(m_element->getFirstChild());
		}
	public:
		bool hasValue()
		{
			DOMText* domTextNode = getTextContent();
			return (domTextNode!=NULL);
		}

		string getStringValue()
		{
			DOMText* domTextNode = getTextContent();
			if (domTextNode==NULL)
				return EmptyString;
			
			return XMLChToString(domTextNode->getData());
		}

		double getDoubleValue()
		{
			return StringToDouble(getStringValue());
		}

		int getIntValue()
		{
			return StringToInt(getStringValue());
		}

		vector<CXMLElement*>* getChildElements()
		{
			vector<CXMLElement*>* childElements = new vector<CXMLElement*>();
			if (m_element->hasChildNodes())
			{
				DOMNodeList* children = m_element->getChildNodes();
				for(unsigned int i=0; i<children->getLength()-1; i++)
				{
					DOMNode* node = children->item(i);
					if( !node->getNodeType() || node->getNodeType() != DOMNode::ELEMENT_NODE )
						continue;
					DOMElement* element = dynamic_cast< xercesc::DOMElement* >( node );
					childElements->push_back(CreateElement(element));
				}
			}

			return childElements;
		}

		tuple<string, string> getAttributes()
		{
			// TODO: more work is required.
			return tuple<string, string>();
		}

		string getAttributeValue( string attributeName ) 
		{
			XMLCh* xtext = new XMLCh[attributeName.length() + 1];
			XMLString::transcode(attributeName.c_str(), xtext, attributeName.length());
			const XMLCh* val = m_element->getAttribute(xtext);
			delete xtext;
			return XMLChToString(val);
		}

		CXMLElement* getElementByAttribute(string attributeName, string attributeValue)
		{
			XMLCh* xAttributeName = new XMLCh[attributeName.length() + 1];
			XMLCh* xAttributeValue = new XMLCh[attributeValue.length() + 1];
			XMLString::transcode(attributeName.c_str(), xAttributeName, attributeName.length());
			XMLString::transcode(attributeValue.c_str(), xAttributeValue, attributeValue.length());
			
			CXMLElement* xmlElement = getElementByAttribute(m_element, xAttributeName, xAttributeValue);
			
			delete xAttributeName;
			delete xAttributeValue;

			return xmlElement;
		}
	private:
		static CXMLElement* getElementByAttribute(DOMElement* element, XMLCh* xAttributeName, XMLCh* xAttributeValue)
		{
			if ( CheckAttribute(element, xAttributeName, xAttributeValue) )
				return CreateElement(element);

			if (!element->hasChildNodes()) return NULL;
			
			DOMNodeList* children = element->getChildNodes();
			for(unsigned int i=0; i<children->getLength()-1; i++)
			{
				DOMNode* node = children->item(i);
				if( !node->getNodeType() || node->getNodeType() != DOMNode::ELEMENT_NODE )
					continue;

				DOMElement* element = dynamic_cast< xercesc::DOMElement* >( node );
				CXMLElement* xmlElement = getElementByAttribute(element, xAttributeName, xAttributeValue);
				if (xmlElement) return xmlElement;
			}

			return NULL;
		}
		static bool CheckAttribute(DOMElement* element, XMLCh* xAttributeName, XMLCh* xAttributeValue)
		{
			if (!element->hasAttribute(xAttributeName)) return false;
			const XMLCh* val = element->getAttribute(xAttributeName);
			bool result = (XMLString::compareString(xAttributeValue, val)==0);

			return result;
		}
		static double StringToDouble(string s)
		{
			istringstream instr(s);
			double x;
			instr >> x;
			return x;
		}

		static int StringToInt(string s)
		{
			istringstream instr(s);
			int x;
			instr >> x;
			return x;
		}

		static string XMLChToString(const XMLCh* arr)
		{
			if (arr==NULL) return EmptyString;
			char* s = XMLString::transcode(arr);
			string r(s);
			XMLString::release(&s);
			return r;
		}

		//static DOMElement* CreateElement(DOMDocument* doc, string name)
		//{
		//	XMLCh* xname = new XMLCh[name.length() + 1];
		//	XMLString::transcode(name.c_str(), xname, name.length());
		//	DOMElement* r = doc->createElement(xname);
		//	delete xname;
		//	return r;
		//}
	};


	class CXMLDocument
	{
		XercesDOMParser* m_parser;
		ErrorHandler* m_errHandler;
		DOMDocument* m_domDocument;
		CXMLElement* m_rootElement;
	private:
		CXMLDocument()
		{
			m_parser = NULL;
			m_errHandler = NULL;
			m_domDocument = NULL;
			m_rootElement = NULL;
		}
	public:
		~CXMLDocument()
		{
			if (m_rootElement)
			{
				delete m_rootElement;
				m_rootElement = NULL;
			}

			if (m_errHandler)
			{
				delete m_errHandler;
				m_errHandler = NULL;
			}

			// DON'T delete m_domDocument, the parser handles deleting documents when the parser gets deleted.

			if (m_parser)
			{
				delete m_parser;
				m_parser = NULL;
			}
		}
	public:
		static CXMLDocument* OpenDocument(string xmlFilePath, string xsdFilePath)
		{
			CXMLDocument* newObject = new CXMLDocument();
			newObject->m_parser = new XercesDOMParser();
			newObject->m_errHandler = (ErrorHandler*) new HandlerBase();
			newObject->m_parser->setErrorHandler(newObject->m_errHandler);

			try {
				newObject->m_parser->setValidationScheme(XercesDOMParser::Val_Always);
				newObject->m_parser->setDoNamespaces(true);
				newObject->m_parser->setDoSchema(true);
				newObject->m_parser->setExternalNoNamespaceSchemaLocation(xsdFilePath.c_str());
				newObject->m_parser->parse(xmlFilePath.c_str());
			}
			catch (const XMLException& toCatch) {
				char* message = XMLString::transcode(toCatch.getMessage());
				cout<< "Exception message is: \n"
					<< message << "\n";
				XMLString::release(&message);
				return NULL;
			}
			catch (const DOMException& toCatch) {
				char* message = XMLString::transcode(toCatch.msg);
				cout<< "Exception message is: \n"
					<< message << "\n";
				XMLString::release(&message);
				return NULL;
			}
			catch (...) {
				cout<< "Unexpected Exception \n" ;
				return NULL;
			}
			
			newObject->m_domDocument = newObject->m_parser->getDocument();
			newObject->m_rootElement = CXMLElement::CreateElement( newObject->m_domDocument->getDocumentElement() );

			return newObject;
		}

		static bool CreateDocument(string filePath, DOMNode* node)
		{
			XMLCh tempStr[100];
			XMLString::transcode("LS", tempStr, 99);
			DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
			DOMLSSerializer* theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer();
		
			// optionally you can set some features on this serializer
			if (theSerializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true))
				theSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTDiscardDefaultContent, true);
		
			if (theSerializer->getDomConfig()->canSetParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true))
				theSerializer->getDomConfig()->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true);
		
			//// optionally you can implement your DOMLSSerializerFilter (e.g. MyDOMLSSerializerFilter)
			//// and set it to the serializer
			//DOMLSSerializer* myFilter = new myDOMLSSerializerFilter();
			//theSerializer->setFilter(myFilter);
		
			//// optionally you can implement your DOMErrorHandler (e.g. MyDOMErrorHandler)
			//// and set it to the serializer
			//DOMErrorHandler* errHandler = new myDOMErrorHandler();
			//theSerializer->getDomConfig()->setParameter(XMLUni::fgDOMErrorHandler, myErrorHandler);
		
			// StdOutFormatTarget prints the resultant XML stream
			// to stdout once it receives any thing from the serializer.
			//XMLFormatTarget *myFormTarget = new StdOutFormatTarget();
			XMLFormatTarget *myFormTarget = new LocalFileFormatTarget("TODO: ");
		
		
			DOMLSOutput* theOutput = ((DOMImplementationLS*)impl)->createLSOutput();
			theOutput->setByteStream(myFormTarget);
		
			try {
				// do the serialization through DOMLSSerializer::write();
				theSerializer->write(node, theOutput);
			}
			catch (const XMLException& toCatch) {
				char* message = XMLString::transcode(toCatch.getMessage());
				cout << "Exception message is: \n"
					<< message << "\n";
				XMLString::release(&message);
				return -1;
			}
			catch (const DOMException& toCatch) {
				char* message = XMLString::transcode(toCatch.msg);
				cout << "Exception message is: \n"
					<< message << "\n";
				XMLString::release(&message);
				return -1;
			}
			catch (...) {
				cout << "Unexpected Exception \n" ;
				return -1;
			}
		
			theOutput->release();
			theSerializer->release();
			//delete myErrorHandler;
			//delete myFilter;
			delete myFormTarget;
			return 0;
		}
		
	public:
		CXMLElement* GetRootElement()
		{
			return m_rootElement;
		}

		CXMLElement* getElementByAttribute(string attributeName, string attributeValue)
		{
			if (!m_rootElement) return NULL;
			m_rootElement->getElementByAttribute(attributeName, attributeValue);
		}
	};

	static bool xhlInitialize() 
	{
		try {
			XMLPlatformUtils::Initialize();
			return true;
		}
		catch (const XMLException&) {
			return false;
		}
	}

	static void xhlTerminate()
	{
		XMLPlatformUtils::Terminate();
	}
};
